package com.slightech.marvin.api.doc.model;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.slightech.marvin.api.doc.annotation.MarvinApiParamField;
import com.slightech.marvin.api.doc.annotation.MarvinApiRequestParam;
import com.slightech.marvin.api.doc.parser.FieldCollection;
import com.slightech.marvin.api.doc.parser.IFieldParser;
import com.slightech.marvin.api.doc.util.JsonTool;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author willardwang
 * @description
 * @date 2019/07/29
 */
@Getter
@Setter
public class ApiRequest {
    private String requestExampleJson;
    private String requestUri;
    private List<ApiField> requestParamList;

    private ApiRequest(Builder builder) {
        this.requestUri = builder.requestUri;
    }

    public static class Builder {
        private String requestUri;
        private Method method;
        private IFieldParser fieldParser;

        public Builder setMethod(Method method) {
            this.method = method;
            return this;
        }

        public Builder setRequestUri(String requestUri) {
            this.requestUri = requestUri;
            return this;
        }

        public Builder setFieldParser(IFieldParser fieldParser) {
            this.fieldParser = fieldParser;
            return this;
        }

        public ApiRequest build() {
            if (this.fieldParser == null) {
                throw new RuntimeException("fieldParse is null");
            }
            if (method == null) {
                throw new RuntimeException("method is null");
            }
            val apiRequest = new ApiRequest(this);
            val marvinApiParam = method.getAnnotation(MarvinApiRequestParam.class);
            List<ApiField> requestParamList = new ArrayList<>();
            Map<String, Object> fieldMap = new HashMap<>();

            Parameter[] pms = method.getParameters();
            Class requestDtoClz = null;
            for (int i = 0; i < pms.length; i++) {
                Parameter parameter = pms[i];
                RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
                if (requestParam != null) {
                    String dataTypeName = parameter.getParameterizedType().getTypeName();
                    if ("data".equals(requestParam.value())) {
                        if (dataTypeName.equals("java.lang.String") || dataTypeName.equals("java.util.Map")) {
                            continue;
                        }
                        requestDtoClz = method.getParameterTypes()[i];
                        break;
                    }
                }
            }

            FieldCollection clazzFieldCollection = null;
            FieldCollection paramFieldCollection = null;
            if (requestDtoClz != null) {
                clazzFieldCollection = this.fieldParser.parseClazzField(requestDtoClz);
            }
            if (null != marvinApiParam) {
                MarvinApiParamField[] marvinApiParamFields = marvinApiParam.field();
                paramFieldCollection = this.fieldParser.parseMarvinApiParamField(marvinApiParamFields);
            }
            if (clazzFieldCollection != null) {
                requestParamList = clazzFieldCollection.getApiFieldList();
                fieldMap = clazzFieldCollection.getApiFieldMap();
            }

            if (paramFieldCollection != null) {
                for (ApiField apiField : paramFieldCollection.getApiFieldList()) {
                    requestParamList.add(apiField);
                }
                for (Map.Entry<String, Object> paramEntry : paramFieldCollection.getApiFieldMap().entrySet()) {
                    fieldMap.put(paramEntry.getKey(), paramEntry.getValue());
                }
            }

            try {
                apiRequest.setRequestExampleJson(JsonTool.bean2JsonStr(fieldMap));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            apiRequest.setRequestParamList(requestParamList);
            return apiRequest;
        }
    }
}
